/*
  Syslog + Web Server
 A simple web server that shows the value of the analog input pins.
 using an Arduino  Ethernet shield. 
 created 16 Mars 2012
 by Cédric2, libre de tous droit.
 Premier programme...
 31376
 v10 petites amélioration
 v11 Correction Bug sendfile work 28270
 V12 bug
 V13 Class maison
 V15 Bug NTP / RF Valide / Bug Lecture fichier temp
 V17 Introduction des lib. accelérations FTP
 v21 FTP tourne
 v22 correction bug ntp client close
 v25 RF to int
 
 */



#include <SPI.h>
#include <Mirf.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
#include <SD.h>
#include "Time_ced.h"
#include "syslog_ced.h"

#include <LiquidCrystal.h>
#include <avr/sleep.h>
#include <avr/wdt.h>
#include <avr/power.h>
#include <avr/interrupt.h>
#include <OneWire.h>
#include "receive.h"


#define def_broche_OneWire 47
OneWire capteur(def_broche_OneWire);

#include "TimedAction.h"
#include "maison.h"

EthernetClient client;

//
#define def_broche_Sdcard 4
#define def_broche_liquidcrystal 5
#define df_hardware_CS 53

#define NoSD2

#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

#define lcd_light 22

//EthernetClient client;
//EthernetClient dclient;
unsigned long code[10];
char buffer[128];


char current_file_conf[] = "C0.T" ;
char num_file_conf = '0' ;


//SdFat card;

//Definition de ma maison

//RCSwitch mySwitch;
Maison MaMaison;

//Ethernet IP

IPAddress ip(192,168,1, 4);
IPAddress syno(192,168,1, 11);

byte mac[] = {   
  0xD0, 0xAD, 0xBE, 0xEF, 0xFE, 0xEA };

#define CED_SWITCH_MAX_CHANGES 70
unsigned int timings[CED_SWITCH_MAX_CHANGES+1]; 
unsigned int repeatCount = 0;
unsigned int changeCount = 0;


// Initialize the Ethernet server library


//Déclaration pour client NTP
#define def_localPort  8888      // local port to listen for UDP/NTP packets
IPAddress freebox(192, 168, 1, 254); // NTP server freebox, donc fonctionne même si adsl down :-)

//IPAddress vb(192, 168, 1, 7);
//buffer to hold incoming and outgoing packets 
// A UDP instance to let us send and receive packets over UDP



//declaration constante de broche 
// crée un objet One Wire sur la broche voulue

// FTP server ip
EthernetServer server(80);
//EthernetClient client;   //ftp and http
//EthernetClient dclient;  //ftp data

//LiquidCrystal_SPI lcd(def_broche_liquidcrystal);
LiquidCrystal lcd(8, 9, 23, 5, 6, 7);
unsigned long ntp_time = 0;
unsigned long milli_at_ntp_time = 0;
unsigned long nb_sleep = 0;
unsigned long est_sleep = 8723;


TimedAction ActiongetNtpTime = TimedAction(120,getNtpTimev);
TimedAction ActiondoFTP = TimedAction(6000,doFTPv);
TimedAction Actionloop_10 = TimedAction(10,loop_10);
TimedAction Actionloop_30 = TimedAction(30,loop_30);
TimedAction Actionloop_60 = TimedAction(60,loop_60);

/*
ISR(WDT_vect) {
 cli();
 wdt_disable();
 p_OK(99);
 sei();
 }
 */

unsigned long est_sec = 0;
unsigned long  keypad_time = 0;

ISR (WDT_vect)
{
  // WDIE & WDIF is cleared in hardware upon entering this ISR
  wdt_disable();
}

void setup()
{
  pinMode(lcd_light, OUTPUT);  
  digitalWrite(lcd_light, HIGH);
  wdt_disable();
  Serial.begin(115200);
  //p_OK(0);
  pinMode(def_broche_RF_T, OUTPUT);
  pinMode(df_hardware_CS, OUTPUT);
  //watchdog 8s
  lcd.begin(20, 4);
  lcd.print(F("START IP"));
  //delay(10000);
  Ethernet.begin( mac, ip);  //initialisation ethernet
  server.begin();  
  //p_OK(1);
  //8 * facility + severity  16/5

  syslog(SYSLOG_Info,"start");
  lcd.setCursor(0, 0);
  lcd.print(F("START SD"));
  if (!SD.begin(def_broche_Sdcard)) {
    error(1);
  }
  lcd.setCursor(0, 0);
  lcd.print(F("SD OK"));
  server.begin();            //initialisation serveur web
  //initialisation Udp pour NTP
  //setSyncInterval(60);
  int i;
  do
  { 
    i =  getNtpTime();
  }
  while (i >0);
  p_OK(3);
  est_sec = get_estimate_time();
  receive_init();
  p_OK(4);
  ActiondoFTP.check(est_sec);
  Actionloop_30.check(est_sec);
  Actionloop_60.check(est_sec);
  //attachInterrupt(0, interruptHandler, CHANGE);
  //wdt_enable(WDTO_8S);
  //8 * facility + severity  16/5
  syslog(SYSLOG_Info,"end start");
  lcd.setCursor(0, 0);
  lcd.print(F("START OK"));
  p_OK(5);
  digitalWrite(lcd_light, LOW);
}



void loop()
{ 

  //wdt_reset();
  httpServer(); 
  // wdt_reset();
  //p_OK(freeRam());
  //    sleep_mode();
  est_sec = get_estimate_time();
  //int temp_mirf[2];
  print_lcd();
  if (!recent_LCD_buttons())
  {

   
      if (!Actionloop_30.check(est_sec))
        if (!Actionloop_60.check(est_sec))
          if (!ActiondoFTP.check(est_sec))
            if (!ActiongetNtpTime.check(est_sec))
             if (!Actionloop_10.check(est_sec))
            {
              // p_OK(6);
              //delay(500);
              /*nb_sleep++;
               set_sleep_mode(SLEEP_MODE_PWR_DOWN);
               wdt_enable(WDTO_8S);
               WDTCSR |= (1 << WDIE);	
               sleep_enable();
               sleep_mode();
               sleep_disable();*/
              /* power_timer1_disable();	
               power_timer0_disable();	
               power_spi_disable();
               power_usart0_disable();
               power_twi_disable();   */
              // p_OK(8);
            }
  }
}

void loop_10()
{
  p_OK(10);
  MaMaison.conf_chauffage(est_sec);
  p_OK(11);
  test_error(send_syslog_relai(MaMaison.get_relay(1,est_sec),MaMaison.get_relay(2,est_sec)));
  p_OK(12);
  struct temp_msg_struct  temp_mirf = get_temp_mirf();
  if (temp_mirf.num_sonde >= 1 && temp_mirf.num_sonde < nb_sondes)
  {
    p_OK(13);
    MaMaison.set_rf_temp(temp_mirf.num_sonde,temp_mirf.temp,est_sec);
    p_OK(14);
  }
  p_OK(15);
}

void test_error (int result)
{
  if (result != 0)
  {
    error(result);
  }
}


void loop_30()
{
  p_OK(30);
  MaMaison.read_conf(num_file_conf,est_sec);
  p_OK(31);
  MaMaison.get_temp_rf(capteur,est_sec);
  p_OK(32);
}

void loop_60()
{
  p_OK(60);

  //syslog(SYSLOG_Notice,print_log());
  send_http(print_log());
  if (error(add_log()))  
    error(63);   
  p_OK(62);
}

// define some values used by the panel and buttons
int lcd_key     = 0;
int adc_key_in  = 0;
#define btnRIGHT  0
#define btnUP     1
#define btnDOWN   2
#define btnLEFT   3
#define btnSELECT 4
#define btnNONE   5

// read the buttons
int read_LCD_buttons()
{
  adc_key_in = analogRead(0);      // read the value from the sensor
  // my buttons when read are centered at these valies: 0, 144, 329, 504, 741
  // we add approx 50 to those values and check to see if we are close
  if (adc_key_in >= 790) return btnNONE; // We make this the 1st option for speed reasons since it will be the most likely result
  keypad_time = est_sec ;
  digitalWrite(lcd_light, HIGH);  
  if (adc_key_in < 50)   return btnRIGHT; 
  if (adc_key_in < 195)  return btnUP;
  if (adc_key_in < 380)  return btnDOWN;
  if (adc_key_in < 555)  return btnLEFT;
  if (adc_key_in < 790)  return btnSELECT;   
  return btnNONE;  // not use when all others fail, return this...
}

byte recent_LCD_buttons()
{
  if (keypad_time + 5 >= est_sec)
  {
    return TRUE;  
  }
  else
  { 
    digitalWrite(lcd_light, LOW);  
    return FALSE; 
  }

}


void print_lcd()
{
  static int current_piece = 0 ;
  lcd.setCursor(0, 0);
  int btn = read_LCD_buttons();
  if (btn == btnDOWN)
    current_piece = min(nb_pieces-1,current_piece+1);
  if (btn == btnUP)
    current_piece = max(0,current_piece-1);
  lcd.print(MaMaison.list_pieces[current_piece].Nom);
  lcd.print(':');
  char text[10]  ;
  snprintf(text,10,"%d.%02d",MaMaison.get_temp_piece(current_piece,est_sec)/100,MaMaison.get_temp_piece(current_piece,est_sec)%100);
  lcd.print(text);
  lcd.print(':');
  snprintf(text,10,"%d",MaMaison.list_pieces[current_piece].target_temp/100);
  lcd.print(text); 
  lcd.print(" "); 
  if (btn != btnNONE)
    delay(1000);
}



byte error(int errno) {
  if (errno!=0)
  {  
    //  digitalWrite(9, HIGH);
    Serial.print(F("E="));
    Serial.println(errno);
    lcd.setCursor(7, 1);
    lcd.print(F("E="));
    lcd.print(errno);
    char text[25];
    snprintf(text,25,"%dh%d ER=%d",hour(est_sec),minute(est_sec),errno);
    syslog(SYSLOG_Alert,text);
    File f = SD.open("E.T", FILE_WRITE);
    if (f) {
      f.print(est_sec);
      f.print(F("E="));
      f.println(errno);
      f.close();
    }

    // set the LED on
  }
  return errno;
}




void p_OK(int errno) {
  char text[30];
  snprintf(text,30," %dh%d OK=%d",hour(est_sec),minute(est_sec),errno);
  Serial.println(text);
  lcd.setCursor(0, 1);
  lcd.print(F("OK="));
  lcd.print(errno);
  lcd.print(F(""));
  File f = SD.open("E.T", FILE_WRITE);
  if (f) {
    f.print(est_sec);
    f.println(text);
    f.close();
  }
  syslog(SYSLOG_Info,text);
  //Serial.println("endok");
}



boolean add_log()
{
  /* char * to_log;
   to_log = print_log();
   Serial.print(to_log);
   File f = SD.open("L.T", FILE_WRITE);
   if (f) {
   f.print(to_log);
   //print_log(&f);
   f.close();
   return 0;
   }*/
  return 0;
}

void print_log(Print* pPrint)
{
  (*pPrint).print(est_sec);
  (*pPrint).print(';');
  (*pPrint).print(millis());
  (*pPrint).print(';');
  for ( byte i = 0; i < nb_chauffages; i++) {
    //(*pPrint).print('c');
    long list[4] = {
      i,
      MaMaison.list_chauffages[i].RF_interrupteur,
      MaMaison.list_pieces[MaMaison.list_chauffages[i].group].target_temp,
      MaMaison.get_temp_piece(MaMaison.list_chauffages[i].group,est_sec)
      };
      for ( byte i = 0; i < 4; i++) {
        (*pPrint).print(list[i]);
        (*pPrint).print(';');
      }
  }
  (*pPrint).println();
}


String print_log()
{
  String log = String(est_sec);
  log+= ":";
  log+=millis();
  log+= ":";
  for ( byte i = 0; i < nb_pieces; i++) {
    //(*pPrint).print('c');
    long list[3] = {
      i,
      MaMaison.list_pieces[i].target_temp,
      MaMaison.get_temp_piece(i,est_sec)
      };
      for ( byte j = 0; j < 3; j++) {
        log+=(list[j]);  
        log+= ':';
      }
  }
  return log; 
}

void send_http(String tosend)
{
  p_OK(70);
  if (client.connect(syno,80))
  {
    client.print(F("GET /nolog/arduw.py?v="));
    client.print(tosend);
    client.println(F(" HTTP/1.1")); 
    client.println(F("User-Agent: Arduino"));
    client.println(F("Accept: text/html"));
    client.println(F("Host: arduino"));
    client.println();
    client.stop(); 
  }
  else
  {
    client.stop(); 
    error(70);
  }
  p_OK(71);
}


byte read_http()
{
  //EthernetClient client;
  p_OK(81);
  if (client.connect(syno,80))
  {
    p_OK(82);
    client.print(F("GET /nolog/conf.txt"));
    client.println(F(" HTTP/1.1")); 
    client.println(F("User-Agent: Arduino"));
    client.println(F("Accept: text/html"));
    client.println(F("Host: arduino"));
    client.println();
    byte num_file_tmp = num_file_conf ;
    if (num_file_tmp++ >= '9')  num_file_tmp = '0' ;
    current_file_conf[1] =  num_file_tmp ;
    p_OK(83);
    delay(100);
    SD.remove(current_file_conf);
    p_OK(84);
    File f = SD.open(current_file_conf, FILE_WRITE);

    if(!f)  return 31;
    char c = client.read();
    unsigned long wdog = millis();
    p_OK(86);
    while(client.connected() && c !='#' )
    {
      while (client.available() && c !='#' )
      {
        c = client.read();
        if (millis()-wdog >= 20000)
        {
          client.stop();
          f.close();
          return(82);
        }   
      }
      if (millis()-wdog >= 20000)
      {
        client.stop();
        f.close();
        return(83);
      }   
    }
    p_OK(87);
    f.write('#');
    unsigned long i = 0;
    while(client.connected()  )
    {
      while( client.available() )
      {
        c = client.read();
        f.write(c);  
        i++; 
        if (millis()-wdog >= 20000)
        {
          client.stop();
          f.close();
          return(84);
        }   
      }
      if (millis()-wdog >= 20000)
      {
        client.stop();
        f.close();
        return(85);
      }   
    }
    p_OK(88);
    client.stop();
    f.close();
    if (i >= 100 && c == '!')
    {
      num_file_conf = num_file_tmp; 
      return(0);
    }
    else
    {
      return(81);
    }

  }
  else
  {
    client.stop();
    return (80);
  }
  p_OK(89);
  return 0;
}



byte getNtpTime()
{
  //EthernetClient client;
  p_OK(91);
  //Serial.println("go connect");
  if (client.connect(syno,80))
  {
    p_OK(92);
    client.print(F("GET /nolog/time.py"));
    client.println(F(" HTTP/1.1")); 
    client.println(F("User-Agent: Arduino"));
    client.println(F("Accept: text/html"));
    client.println(F("Host: arduino"));
    client.println();
    p_OK(93);
    delay(100);
    char c = client.read();
    unsigned long wdog = millis();
    p_OK(94);
    while(client.connected() && c !='#' )
    {
      while (client.available() && c !='#' )
      {
        c = client.read();
        if (millis()-wdog >= 20000)
        {
          client.stop();
          error(90);
          return(90);
        }   
      }
      if (millis()-wdog >= 20000)
      {
        client.stop();
        error(91);
        return(91);
      }   
    }
    p_OK(95);
    unsigned long epoch = 0;
    while(client.connected() && c !='!' )
    {
      while( client.available() && c !='!')
      {
        c = client.read();
        {  
          if (c > '9' or c < '0' )
          {
            //break; //(2) ;  //cest pas des nombres !
          }
          else
          {
            epoch = epoch*10+c-'0';
          }
        }
        if (millis()-wdog >= 20000)
        {
          client.stop();
          error(92);
          return(92);
        }   
      }
      if (millis()-wdog >= 20000)
      {
        client.stop();
        error(93);
        return(93);
      }   
    }
    p_OK(96);
    client.stop();
    p_OK(97);
    epoch += 3600;
    if (nb_sleep == 0)
      est_sleep = 8000;
    else
      est_sleep = (est_sleep + ((epoch - ntp_time)*1000 - millis()+milli_at_ntp_time) / nb_sleep)/2 ;                        
    //Serial.print("new est_sleep =");
    //Serial.println(est_sleep);
    //unsigned long time = 1352147043 + millis()/1000 ;
    milli_at_ntp_time = millis();
    nb_sleep = 0;
    ntp_time =  epoch;   
    return(0);
  }
  else
  {
    error(97);
    client.stop();
    return(97);

  }

}

void httpServer() {
  //EthernetClient client;
  //EthernetClient client;
  client = server.available();
  if (client) {
    char gethttp[12] = "";
    byte gethttppos=0;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();          
        if ((c == '\n') || (c == ' ' && gethttppos>=5)) {
          if (strcmp(gethttp, ("/")) == 0) {
            send_file("w/hd.t", &client );

            //client.print(print_log());
            print_log(&client);
            send_file("w/hf.t", &client );
          } 
          else if (strcmp(gethttp, "/a") == 0)
          {  
            print_log(&client);
            //client.print(print_log());
          }          
          else if (strcmp(gethttp, "/l.txt") == 0)
          {  
            send_file("w/ht.t", &client );
            send_file("L.T", &client );
          }
          else if (strcmp(gethttp, "/e.txt") == 0)
          {  
            send_file("w/ht.t", &client );
            send_file("E.T", &client );
          }
          else if (strcmp(gethttp, "/del") == 0)
          {  
            SD.remove("L.T");
            SD.remove("E.T");
            //f.open("L.T",O_WRITE);

            send_file("w/d.t", &client );
          }
          else if (strcmp(gethttp, "/rst") == 0)
          {  
            send_file("w/d.t", &client );
            delay(15000);  //watchdog va faire reseter !
          }
          else if (strcmp(gethttp, "/ftp") == 0)
          {  

            send_file("w/d.t", &client );
            client.stop();
            delay(100);
            doFTPv();
            MaMaison.read_conf(num_file_conf,est_sec);
            return;
          }

          else {
            send_file("w/w3.t", &client );
          }
          break;
        }
        if (gethttppos>=4) {
          gethttp[gethttppos-4] = c;
        }
        gethttppos++;
      }
      if (gethttppos >= 15) {
        send_file("w/w4.t", &client );
        break;
      }
    }
    // give the web browser time to receive the data
    delay(1);   
  }
  client.stop();
}

boolean send_file(const char* file,EthernetClient* pclient )
{

  File f = SD.open(file, FILE_READ);
  if (f) {
    // read from the file until there's nothing else in it:

    //int16_t c;
    //while ((c = f.read()) > 0) client.write((char)c);
    byte n;
    int i = 0 ;
    int  j = 0 ;
    while ((n = f.read(buffer,16)) > 0) 
    {
      buffer[n] = 0;
      (*pclient).print(buffer);
      if (i++ == 1000) { 
        //wdt_reset();
        j++;
        i=0;
        if (j == 1000)
          delay(30000);
      }
    }
    //close the file:
    f.close();
    return true;   
  }
  return false;
}

unsigned long  get_estimate_time() { 
  //p_OK(8);
  unsigned long time = ntp_time + (millis()-milli_at_ntp_time)/1000 + nb_sleep * est_sleep /1000 ;
  return time;
}




int freeRam () {                              //verification memoire libre
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}



//char current_file_conf[] = "C0.T" ;
//char num_file_conf = '0' ;

void doFTPv()
{
  byte result ;
  p_OK(40);
  //if((result=doFTP())==0) 
  if((result=read_http())==0) 
  {
    p_OK(41);
  }
  else
  { 
    error(result);
  }
  p_OK(42);
}

void getNtpTimev()
{
  byte result ;

  //if((result=doFTP())==0) 
  if((result=getNtpTime())==0) 
  {
    p_OK(20);
  }
  else
  { 
    error(result);
  }
  p_OK(21);
}

void interruptHandler2()
{
  long time = micros();
  static unsigned long lastTime = 0;
  unsigned long duration = time - lastTime;
  if (duration <= 50) return;
  static byte repeatCount = 0;
  static byte changeCount = 0;
  duration = time - lastTime;
  if (duration > 9300 and  duration < 9600) //&& duration > timings[0] - 200 && duration < timings[0] + 200)
  {
    if (repeatCount == 1 and changeCount >= 56) 
    {
      // detachInterrupt(0);
      analyse(changeCount); 
      // attachInterrupt(0, interruptHandler, CHANGE);
    }
    repeatCount = 1;
    changeCount = 0;
  } 
  else if (duration > 8000 or changeCount >= CED_SWITCH_MAX_CHANGES)
  {   
    changeCount = 0;
    repeatCount = 0 ;
  }
  timings[changeCount++] = duration;
  lastTime = time;  
}

void analyse(unsigned int changeCount)
{
  byte status = 1;
  long error = 0;
  byte bits = 0;
  static byte num_code = 0 ;
  unsigned long code_tmp = 0;
  for(int i = 1; i< changeCount;i++)
  {
    unsigned long duration;
    duration = timings[i] + error ;
    if (status ==1 and duration >= 440 and duration <= 520*1.2)
    {
      status = 2 ;
      error = 0;
    }
    else if (status ==1 and duration <= 440)
    {
      error = error + duration ;	
    }
    else if  (status ==1 and duration > 440)
    {
      error = duration-520 ;
      status = 2;
    }
    else if (status == 2 and duration >= 1920 * 0.9 and duration <= 1920 * 1.1 )
    {
      status = 1;
      error = 0;
      code_tmp = code_tmp << 1 ;
      bits++;
      // 1 décalage de trop !!
    }
    else if  (status == 2 and duration >= 4496 * 0.9 and duration <= 4496 * 1.1 )
    {
      status = 1;
      error = 0;
      code_tmp = code_tmp << 1 ;
      code_tmp+=1;
      // 1 décalage de trop !!
      bits++;
    }
    else if (status == 2 and duration >= 4496 * 1.1)
    {
      error = 0 ;
      status = 1 ;
    }
    else if (status == 2 )
    {
      error = error + duration ;
    }	
  } 
  if (bits ==28 and code[num_code] != code_tmp)
  {
    num_code = (num_code  + 1)%10 ;
    code[num_code] = code_tmp;
  }
}


















